package com.ayf.payment.game.api.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.ayf.payment.game.api.cache.redis.RedisCacheUtil;
import com.ayf.payment.game.api.dto.*;
import com.ayf.payment.game.api.dto.criteria.*;
import com.ayf.payment.game.api.dto.platform.NetConfigDTO;
import com.ayf.payment.game.api.entity.GameInfo;
import com.ayf.payment.game.api.exception.ParameterException;
import com.ayf.payment.game.api.httpclient.HttpUtils;
import com.ayf.payment.game.api.service.*;
import com.ayf.payment.game.api.service.platform.NetConfigService;
import com.ayf.payment.game.api.service.platform.WxConfigService;
import com.ayf.payment.game.api.type.CommonConstants;
import com.ayf.payment.game.api.utils.CommonResult;
import com.ayf.payment.game.api.utils.DateUtils;
import com.ayf.payment.game.api.utils.FtpUtil;
import com.ayf.payment.game.api.utils.SnowFlakeUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.swwx.charm.commons.lang.utils.BeanConvertUtils;
import com.swwx.charm.commons.lang.utils.LogPortal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Author: zh-liang
 * @Date : 2021-05-08 16:46
 */
@RestController
@RequestMapping("/v/gameGift")
public class GameInfoGiftController {
    @Autowired
    private MerchantConfigService merchantConfigService;
    @Autowired
    private GameInfoGiftService gameInfoGiftService;
    @Autowired
    private GameInfoService gameInfoService;
    @Autowired
    private MerchantGiftLogService merchantGiftLogService;
    @Autowired
    private NetConfigService netConfigService;
    @Autowired
    private GameInfoGiftLogService gameInfoGiftLogService;
    @Autowired
    private PartitionService partitionService;
    @Autowired
    private WxConfigService wxConfigService;
    @Autowired
    private RedisCacheUtil redisCacheUtil;
    @Autowired
    private MerchantLevelRelationService merchantLevelRelationService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    final ExecutorService giftQueryTaskExecutor = Executors.newCachedThreadPool();
    /**
     * 查询红包列表链接
     */
    @GetMapping("/getGiftListLink")
    public CommonResult<String> getGiftListLink(MerchantDTO merchantDTO) {
        MerchantConfigDTO configDTO = merchantConfigService.getMerchantConfigDTO(merchantDTO.getId());
        if (configDTO.getGiftStatus() == 0) {
            return new CommonResult<>(199, "红包功能尚未开启");
        }
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "获取成功", "/v/gameGift/list/" + configDTO.getUniqueId());
    }

    @GetMapping("/list/{uniqueId}")
    public CommonResult<List<ExtGameInfoDTO>> list(@PathVariable("uniqueId") String uniqueId) {
        MerchantConfigDTO merchantConfigDTO = merchantConfigService.getConfigByUniqueId(uniqueId);
        if (merchantConfigDTO == null || merchantConfigDTO.getGiftStatus() == 0) {
            throw new ParameterException("红包功能暂未开启");
        }
        List<MerchantLevelRelationDTO> listId = merchantLevelRelationService.selectDTOBySupMerchantId(merchantConfigDTO.getMerchantId());
        List<Integer> idList = new ArrayList<>();
        listId.forEach(l -> idList.add(l.getMerchantId()));
        idList.add(merchantConfigDTO.getMerchantId());
        GameInfoCriteria gameInfo = new GameInfoCriteria();
        gameInfo.setIdList(idList);
        gameInfo.setGiftStatus(1);
        List<GameInfoDTO> list = gameInfoService.select(gameInfo);
        List<ExtGameInfoDTO> dtoList = BeanConvertUtils.convert(list, ExtGameInfoDTO.class);
        List<ExtGameInfoDTO> dtoList2 = new ArrayList<>();
        NetConfigDTO netConfig = netConfigService.getNetConfig();

        for (ExtGameInfoDTO dto : dtoList) {
            dto.setImgUrl(netConfig.getDomainName() + "/gameapip/v/gameGift/downloadImg?fileName=" + dto.getImg());
            dto.setLogApi(netConfig.getDomainName() + "/gameapip/v/gameGift/collectRecord/" + uniqueId + "?id=" + dto.getId());
            List<GameInfoGiftDTO> list1 = gameInfoGiftService.selectByGameInfoId(dto.getId());
            GameInfoGiftDTO nowGift = null;
            int len = list1.size();
            for (int i=0;i<len;i++) {
                GameInfoGiftDTO infoGiftDTO = list1.get(i);
                int startTime = Integer.parseInt(infoGiftDTO.getStartTime().replaceAll(":", ""));
                int endTime = Integer.parseInt(infoGiftDTO.getEndTime().replaceAll(":", ""));
                Integer nowTime = DateUtils.dateToInteger(new Date(), "HHmmss");
                if (nowTime >= startTime && nowTime <= endTime) {
                    nowGift = infoGiftDTO;
                    break;
                }
                if (nowTime < startTime) {
                    nowGift = infoGiftDTO;
                    break;
                }
                if (i==(len-1)){
                    if (nowTime>endTime) {
                        nowGift = infoGiftDTO;
                        break;
                    }
                }
            }
            if (nowGift != null) {
                dto.setStartTime(nowGift.getStartTime());
                dto.setEndTime(nowGift.getEndTime());
                dto.setTotal(nowGift.getTotal());
                dto.setUsed(nowGift.getUsed());
                dto.setAmount(nowGift.getAmount());
                dtoList2.add(dto);
                dto.setGiftId(nowGift.getId());
            }
        }
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "查询游戏红包列表成功", dtoList2);
    }

    @GetMapping("/list/page/{uniqueId}/{pageNum}/{pageSize}")
    public CommonResult<PageInfo> listPage(@PathVariable("uniqueId") String uniqueId,@PathVariable("pageNum") int pageNum,@PathVariable("pageSize") int pageSize) {
        MerchantConfigDTO merchantConfigDTO = merchantConfigService.getConfigByUniqueId(uniqueId);
        if (merchantConfigDTO == null || merchantConfigDTO.getGiftStatus() == 0) {
            throw new ParameterException("红包功能暂未开启");
        }
        List<MerchantLevelRelationDTO> listId = merchantLevelRelationService.selectDTOBySupMerchantId(merchantConfigDTO.getMerchantId());
        List<Integer> idList = new ArrayList<>();
        listId.forEach(l -> idList.add(l.getMerchantId()));
        idList.add(merchantConfigDTO.getMerchantId());

        GameInfoPageCriteria gameInfoPageCriteria = new GameInfoPageCriteria();
        gameInfoPageCriteria.setPageNum(pageNum);
        gameInfoPageCriteria.setPageSize(pageSize);
        gameInfoPageCriteria.setIdList(idList);
        gameInfoPageCriteria.setGiftStatus(1);
        PageHelper.startPage(gameInfoPageCriteria.getPageNum(),gameInfoPageCriteria.getPageSize());
        List<GameInfoDTO> list = gameInfoService.selectPage(gameInfoPageCriteria);
        PageInfo pageInfo = new PageInfo(list);
        long maxPageSize = pageInfo.getTotal();
        PageHelper.clearPage();
        List<ExtGameInfoDTO> dtoList = BeanConvertUtils.convert(list, ExtGameInfoDTO.class);

        List<ExtGameInfoDTO> dtoList2 = new ArrayList<>();
        NetConfigDTO netConfig = netConfigService.getNetConfig();
        int size = dtoList.size();
        final CountDownLatch countDownLatch = new CountDownLatch(size);

        for (ExtGameInfoDTO dto : dtoList) {
            giftQueryTaskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    dto.setImgUrl(netConfig.getDomainName() + "/gameapip/v/gameGift/downloadImg?fileName=" + dto.getImg());
                    dto.setLogApi(netConfig.getDomainName() + "/gameapip/v/gameGift/collectRecord/" + uniqueId + "?id=" + dto.getId());
                    List<GameInfoGiftDTO> list1 = gameInfoGiftService.selectByGameInfoId(dto.getId());
                    GameInfoGiftDTO nowGift = null;
                    int len = list1.size();
                    for (int i=0;i<len;i++) {
                        GameInfoGiftDTO infoGiftDTO = list1.get(i);
                        int startTime = Integer.parseInt(infoGiftDTO.getStartTime().replaceAll(":", ""));
                        int endTime = Integer.parseInt(infoGiftDTO.getEndTime().replaceAll(":", ""));
                        Integer nowTime = DateUtils.dateToInteger(new Date(), "HHmmss");
                        if (nowTime >= startTime && nowTime <= endTime) {
                            nowGift = infoGiftDTO;
                            break;
                        }
                        if (nowTime < startTime) {
                            nowGift = infoGiftDTO;
                            break;
                        }
                        if (i==(len-1)){
                            if (nowTime>endTime) {
                                nowGift = infoGiftDTO;
                                break;
                            }
                        }
                    }
                    if (nowGift != null) {
                        dto.setStartTime(nowGift.getStartTime());
                        dto.setEndTime(nowGift.getEndTime());
                        dto.setTotal(nowGift.getTotal());
                        dto.setUsed(nowGift.getUsed());
                        dto.setAmount(nowGift.getAmount());
                        dtoList2.add(dto);
                        dto.setGiftId(nowGift.getId());
                    }
                    countDownLatch.countDown();
                }
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            LogPortal.error("{} 游戏红包列表 countDownLatch.await 发生异常",e,uniqueId);
        }
        pageInfo.setList(dtoList2);
        pageInfo.setTotal(maxPageSize);
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "查询游戏红包列表成功",pageInfo);
    }

    @GetMapping("/collectRecord/{uniqueId}")
    public CommonResult<List<MerchantGiftLogDTO>> collectRecord(@PathVariable("uniqueId") String uniqueId, @RequestParam("id") String id) {
        MerchantConfigDTO merchantConfigDTO = merchantConfigService.getConfigByUniqueId(uniqueId);
        if (merchantConfigDTO.getGiftStatus() == 0) {
            throw new ParameterException("红包功能暂未开启");
        }
        List<MerchantLevelRelationDTO> listId = merchantLevelRelationService.selectDTOBySupMerchantId(merchantConfigDTO.getMerchantId());
        List<Integer> idList = new ArrayList<>();
        listId.forEach(l -> idList.add(l.getMerchantId()));
        idList.add(merchantConfigDTO.getMerchantId());
        MerchantGiftLogCriteria cri = MerchantGiftLogCriteria.builder()
                .idList(idList)
                .gameInfoId(id)
                .build();
        cri.setPageNum(1);
        cri.setPageSize(100);
        PageHelper.startPage(cri.getPageNum(), cri.getPageSize());
        List<MerchantGiftLogDTO> list = merchantGiftLogService.selectList(cri);
        if (!CollectionUtils.isEmpty(list)){
            StringBuilder accountStr = new StringBuilder();
            list.forEach(item->{
                accountStr.delete(0,accountStr.length());// 清空内容
                String accountPrefix = item.getAccount().substring(0,2);
                int len = item.getAccount().length()-2;
                len=len<0?0:len;
                for (int i=0;i<len;i++){
                    accountStr.append("*");
                }
                item.setAccount(accountPrefix.concat(accountStr.toString()));// 玩家账号脱敏，只保留前2位
            });
        }
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "查询成功", list);
    }

    /**
     * 游戏红包领取记录查询
     */
    @PostMapping("/merchantGiftLogList")
    public CommonResult<PageDTO> merchantGiftLogList(MerchantDTO merchantDTO, @RequestBody MerchantGiftLogCriteria cri) {
        cri.setMerchantId(merchantDTO.getId());
        PageHelper.startPage(cri.getPageNum(), cri.getPageSize());
        List<MerchantGiftLogDTO> list = merchantGiftLogService.selectList(cri);
        PageInfo<MerchantGiftLogDTO> pageInfo = new PageInfo<>(list);
        PageDTO dto = BeanConvertUtils.convert(pageInfo, PageDTO.class);
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "查询成功", dto);
    }

    /**
     * 游戏红包领取汇总查询
     */
    @PostMapping("/selectGameInfoGiftLog")
    public CommonResult<PageDTO> selectGameInfoGiftLog(MerchantDTO merchantDTO, @RequestBody GameInfoGiftLogPageCriteria criteria) {
        criteria.setMerchantId(merchantDTO.getId());
        PageHelper.startPage(criteria.getPageNum(), criteria.getPageSize());
        List<GameInfoGiftLogDTO> list = gameInfoGiftLogService.selectList(criteria);
        PageInfo<GameInfoGiftLogDTO> pageInfo = new PageInfo<>(list);
        PageDTO dto = BeanConvertUtils.convert(pageInfo, PageDTO.class);
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "查询成功", dto);
    }

    /**
     * 查询红包分区列表
     */
    @PostMapping("/partitionList")
    public CommonResult<List<PartitionDTO>> partitionList(@RequestBody GameInfoGiftDTO criteria) {
        if (StrUtil.isEmpty(criteria.getGameInfoId())) {
            throw new ParameterException("gameInfoId不能为空");
        }
        if (criteria.getId() == null) {
            throw new ParameterException("Id不能为空");
        }
        GameInfoGiftDTO byId = gameInfoGiftService.getById(criteria.getId());
        if (byId == null) {
            throw new ParameterException("Id错误");
        }
        GameInfo gameInfo = gameInfoService.getById(criteria.getGameInfoId());
        if (gameInfo == null) {
            throw new ParameterException("gameInfoId错误");
        }
        PartitionSelectCriteria cri = new PartitionSelectCriteria();
        cri.setGameInfoId(criteria.getGameInfoId());
        cri.setMerchantId(gameInfo.getMerchantId());
        List<PartitionDTO> list = partitionService.selectByGameId(cri);
        List<PartitionDTO> listDTO = new ArrayList<>();
        if (StrUtil.isEmpty(byId.getGameCurrency())) {
            listDTO.addAll(list);
        } else {
            String[] gameCurrencyNameList = byId.getGameCurrency().split("_");
            for (PartitionDTO dto : list) {
                for (String name : gameCurrencyNameList) {
                    if (dto.getGameCurrencyName().toLowerCase().equals(name)) {
                        listDTO.add(dto);
                        break;
                    }
                }
            }
        }
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "查询成功", listDTO);
    }

    /**
     * 领取红包
     */
    @PostMapping(value = "/receive")
    public CommonResult<Map<String, Object>> receive(@RequestBody MerchantGiftLogDTO dto) {
        if (StrUtil.isEmpty(dto.getCode())) {
            throw new ParameterException("code不能为空");
        }
        if (StrUtil.isEmpty(dto.getUniqueId())) {
            throw new ParameterException("uniqueId不能为空");
        }
        if (StrUtil.isEmpty(dto.getPartitionId())) {
            throw new ParameterException("请选择一个分区");
        }
        if (StrUtil.isEmpty(dto.getAccount())) {
            throw new ParameterException("游戏账号不能为空");
        }
        if (dto.getId() == null) {
            throw new ParameterException("Id不能为空");
        }
        MerchantConfigDTO merchantConfigDTO = merchantConfigService.getConfigByUniqueId(dto.getUniqueId());
        if (merchantConfigDTO.getGiftStatus() == 0) {
            throw new ParameterException("领取失败，红包功能暂未开启");
        }

        GameInfoGiftDTO gift = gameInfoGiftService.getById(dto.getId());
        if (gift.getUsed() >= gift.getTotal()) {
            throw new ParameterException("当前红包已被抢光，换个红包试试吧");
        }
        int startTime = Integer.parseInt(gift.getStartTime().replaceAll(":", ""));
        int endTime = Integer.parseInt(gift.getEndTime().replaceAll(":", ""));
        Integer nowTime = DateUtils.dateToInteger(new Date(), "HHmmss");
        if (nowTime < startTime) {
            throw new ParameterException("当前红包活动还未开始");
        }
        if (nowTime > endTime) {
            throw new ParameterException("当前红包活动已经结束");
        }

        WxConfigDTO config = wxConfigService.selectByMId(merchantConfigDTO.getMerchantId());
        String traceId = SnowFlakeUtils.getTraceId();
        String openid = "";
        try {
            openid = redisCacheUtil.getObjectFromCache("OPENID_" + dto.getCode());
            if (StrUtil.isEmpty(openid)) {
                Map<String, String> map = HttpUtils.doGet(traceId, config.getNetUrl() + "/wxapi/wx/gateway/getOpenId?code=" + dto.getCode() + "&merchantId=" + merchantConfigDTO.getMerchantId());
                if ("200".equals(map.get("code"))) {
                    JSONObject data = JSONObject.parseObject(map.get("data"));
                    openid = data.getString("data");
                    if (StrUtil.isEmpty(openid)) {
                        LogPortal.error("{} 领取红包失败，openId为空", traceId);
                        throw new ParameterException("领取红包失败，错误码：" + traceId);
                    }
                } else {
                    LogPortal.error("{} 领取红包失败，返回code不是200", traceId);
                    throw new ParameterException("领取红包失败，错误码：" + traceId);
                }
            }
        } catch (Exception e) {
            LogPortal.error("{} 领取红包失败，发生{}异常，异常信息为：{}", e, traceId, e.getClass().getSimpleName(), e.getMessage());
            throw new ParameterException("领取红包失败，错误码：" + traceId);
        }
        String key = "GIFT_" + DateUtils.dateToInteger(new Date()) + "_" + gift.getGameInfoId() + "_" + openid;
        String dayKey = "GIFT_DAY_ID" + DateUtils.dateToInteger(new Date()) + "_" + merchantConfigDTO.getMerchantId() + "_" + openid;

        String accountKey = "GIFT_ACCOUNT_" + DateUtils.dateToInteger(new Date()) + "_" + gift.getGameInfoId() + "_" + dto.getAccount();
        String dayAccountKey = "GIFT_DAY_ACCOUNT_ID" + DateUtils.dateToInteger(new Date()) + "_" + merchantConfigDTO.getMerchantId() + "_" + dto.getAccount();
        int giftLimitCount;
        if (merchantConfigDTO.getGiftLimitCount()!=null && (giftLimitCount=merchantConfigDTO.getGiftLimitCount().intValue())>0){
            String tipMsg = "当天同一游戏每人最多领取1个红包";
            setGiftLimitCount(key, tipMsg,1);
            tipMsg = "当天同一人最多领取"+giftLimitCount+"个红包";
            setGiftLimitCount(dayKey, tipMsg,giftLimitCount);

            tipMsg = "当天同一游戏每人最多领取1个红包";
            setGiftLimitCount(accountKey, tipMsg,1);
            tipMsg = "当天同一人最多领取"+giftLimitCount+"个红包";
            setGiftLimitCount(dayAccountKey, tipMsg,giftLimitCount);
        }
        dto.setOpenId(openid);
        gameInfoGiftService.giftReceive(dto);
        return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "领取成功");
    }

    private void setGiftLimitCount(String key, String tipMsg,int giftLimitCount) {
        Long increment = stringRedisTemplate.opsForValue().increment(key, 1);
        LogPortal.info("key:{} 当前累计次数:{} 限制次数:{}",key,increment,giftLimitCount);
        if (increment >giftLimitCount) {
            stringRedisTemplate.opsForValue().increment(key,-1);
            throw new ParameterException(tipMsg);
        }
        stringRedisTemplate.expire(key, 1L, TimeUnit.DAYS);
    }

    /**
     * 游戏图片上传
     */
    @ResponseBody
    @RequestMapping(method = RequestMethod.POST, value = "/uploadImg")
    public CommonResult<String> upload(@RequestParam("file") MultipartFile file, MerchantDTO merchantDTO) {
        String traceId = SnowFlakeUtils.getUniqueId();
        String suffix = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf(".") + 1);
        if (!"jpg".equalsIgnoreCase(suffix) && !"png".equalsIgnoreCase(suffix) && !"jpeg".equalsIgnoreCase(suffix) && !"gif".equalsIgnoreCase(suffix)) {
            LogPortal.error("{} 商户[{}]图片上传失败，文件格式错误，仅支持jpg、png、jpeg、gif，当前文件格式：{}", traceId, merchantDTO.getId(), suffix);
            throw new ParameterException("上传失败，图片格式错误，仅支持jpg、png、jpeg、gif格式");
        }
        if (file.getSize() > 1024 * 1100) {
            return new CommonResult<>(CommonConstants.RETURN_ERROR, "图片过大，仅支持1M之下的图片");
        }
        suffix = suffix.toLowerCase();
        String fileName = traceId.concat(".").concat(suffix);
        try {
            FtpUtil.uploadXYFile(file.getBytes(), fileName, "",traceId);
            return new CommonResult<>(CommonConstants.RETURN_SUCCESS, "上传成功", fileName);
        } catch (Exception e) {
            LogPortal.error("[{}]图片上传发生[{}]异常,异常信息为[{}]", e, traceId, e.getClass().getSimpleName(), e.getMessage());
            return new CommonResult<>(CommonConstants.RETURN_ERROR, "上传失败，错误码：" + traceId);
        }
    }

    /**
     * 文件下载
     *
     * @param fileName
     * @return
     */
    @ResponseBody
    @RequestMapping(method = RequestMethod.GET, value = "/downloadImg")
    public void downloadFile(HttpServletResponse response, @RequestParam("fileName") String fileName) {
        String traceId = SnowFlakeUtils.getUniqueId();
        ServletOutputStream outputStream = null;
        try {
            byte[] bytes = FtpUtil.downloadXYFile("", fileName);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            response.setHeader("Cache-Control", "No-cache");
            outputStream = response.getOutputStream();
            outputStream.write(bytes);
        } catch (Exception e) {
            LogPortal.error("[{}]文件下载发生[{}]异常,异常信息为[{}]", e, traceId, e.getClass().getSimpleName(), e.getMessage());
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (IOException e) {
                LogPortal.error("文件上传关闭流时发生IOException，异常信息为: {}", e, e.getMessage());
            }
        }
    }
}
